---
title: Лучшие практики
image: /images/user-guide/tips/light-bulb.png
---

<Frame>
  <img src="/images/user-guide/tips/light-bulb.png" alt="Header" />
</Frame>

Этот документ описывает лучшие практики, которых следует придерживаться при работе с фронтендом.

## Управление состоянием

React и Recoil отвечают за управление состоянием в коде.

### Используйте `useRecoilState` для хранения состояния

Полезно создавать столько атомов, сколько вам нужно для хранения состояния.

<Warning>

Лучше использовать дополнительные атомы, чем пытаться быть слишком кратким с передачей свойств.

</Warning>

```tsx
export const myAtomState = atom({
  key: 'myAtomState',
  default: 'default value',
});

export const MyComponent = () => {
  const [myAtom, setMyAtom] = useRecoilState(myAtomState);

  return (
    <div>
      <input
        value={myAtom}
        onChange={(e) => setMyAtom(e.target.value)}
      />
    </div>
  );
}
```

### Не используйте `useRef` для хранения состояния

Избегайте использования `useRef` для хранения состояния.

Если вы хотите сохранить состояние, вам следует использовать `useState` или `useRecoilState`.

Смотрите [как управлять повторными рендерами](#managing-re-renders), если вы считаете, что вам нужен `useRef`, чтобы предотвратить их.

## Управление повторными рендерами

Управлять повторными рендерами в React может быть сложно.

Вот некоторые правила, которые стоит соблюдать, чтобы избегать ненужных повторных рендеров.

Помните, что вы всегда можете избежать повторных рендеров, если поймете причину их возникновения.

### Работа на корневом уровне

Избежать повторных рендеров в новых функциях стало проще, устранив их на корневом уровне.

Сайдкар-компонент `PageChangeEffect` содержит всего один `useEffect`, который реализует всю логику при изменении страницы.

Таким образом, вы знаете, что есть только одно место, которое может вызвать повторный рендер.

### Всегда думайте дважды, прежде чем добавлять `useEffect` в ваш код

Повторные рендеры часто вызываются ненужными `useEffect`.

Подумайте, нужно ли вам использовать `useEffect`, или же вы можете перенести логику в функцию обработчика событий.

Как правило, несложно перенести логику в функции `handleClick` или `handleChange`.

Вы также можете найти их в библиотеках, таких как Apollo: `onCompleted`, `onError` и т. д.

### Используйте дополнительный компонент для извлечения `useEffect` или логики получения данных

Если вы считаете, что нужно добавить `useEffect` в корневой компонент, стоит рассмотреть возможность его извлечения в сайдкар-компонент.

Вы можете применять то же самое для логики получения данных, с хуками Apollo.

```tsx
// ❌ Bad, will cause re-renders even if data is not changing, 
//    because useEffect needs to be re-evaluated
export const PageComponent = () => {
  const [data, setData] = useRecoilState(dataState);
  const [someDependency] = useRecoilState(someDependencyState);

  useEffect(() => {
    if(someDependency !== data) {
      setData(someDependency);
    }
  }, [someDependency]);

  return <div>{data}</div>;
};

export const App = () => (
  <RecoilRoot>
    <PageComponent />
  </RecoilRoot>
);
```

```tsx
// ✅ Good, will not cause re-renders if data is not changing, 
//   because useEffect is re-evaluated in another sibling component
export const PageComponent = () => {
  const [data, setData] = useRecoilState(dataState);

  return <div>{data}</div>;
};

export const PageData = () => {
  const [data, setData] = useRecoilState(dataState);
  const [someDependency] = useRecoilState(someDependencyState);

  useEffect(() => {
    if(someDependency !== data) {
      setData(someDependency);
    }
  }, [someDependency]);

  return <></>;
};

export const App = () => (
  <RecoilRoot>
    <PageData />
    <PageComponent />
  </RecoilRoot>
);
```

### Используйте состояния семейства Recoil и селекторы семейства Recoil

Состояния семейства Recoil и селекторы — отличный способ избежать повторных рендеров.

Они полезны, когда нужно хранить список элементов.

### Не следует использовать `React.memo(MyComponent)`

Избегайте использования `React.memo()`, так как это не решает причину повторного рендера, но разрывает цепочку рендера, что может привести к неожиданному поведению и усложнить рефакторинг кода.

### Ограничьте использование `useCallback` или `useMemo`

Часто это не нужно и делает код труднее читаемым и поддерживаемым для улучшения производительности, которую сложно заметить.

## Console.logs

`console.log` полезны во время разработки, предоставляя информацию в реальном времени о значениях переменных и потоке кода. Однако оставление их в коде для продакшена может привести к нескольким проблемам:

1. **Производительность**: Избыточное логирование может повлиять на производительность, особенно в клиентских приложениях.

2. **Безопасность**: Логирование конфиденциальных данных может раскрыть критическую информацию тем, кто может просматривать консоль браузера.

3. **Чистота**: Заполнение консоли логами может скрыть важные предупреждения или ошибки, которые нужно увидеть разработчикам или инструментам.

4. **Профессионализм**: Конечные пользователи или клиенты, проверяющие консоль и видящие множество логов, могут усомниться в качестве и обработке кода.

Убедитесь, что все `console.logs` удалены перед загрузкой кода в продакшен.

## Называние

### Наименование переменных

Названия переменных должны точно описывать их цель или функцию.

#### Проблемы с универсальными именами

Универсальные имена в программировании не идеальны, так как они не хватает определенности, что ведет к неясности и ухудшению читаемости кода. Такие имена не передают цель переменной или функции, делая трудным понимать намерение кода без более глубокого исследования. Это может привести к увеличению времени отладки, повышению вероятности ошибок и трудностям в поддержке и сотрудничестве. Между тем, описательные имена делают код самодокументирующимся и более простым для навигации, улучшая качество кода и продуктивность разработчиков.

```tsx
// ❌ Bad, uses a generic name that doesn't communicate its
//    purpose or content clearly
const [value, setValue] = useState('');
```

```tsx
// ✅ Good, uses a descriptive name
const [email, setEmail] = useState('');
```

#### Некоторые слова, которых стоит избегать в названиях переменных

- dummy

### Обработчики событий

Названия обработчиков событий должны начинаться с `handle`, в то время как `on` используется как префикс для наименования событий в пропсах компонентов.

```tsx
// ❌ Bad
const onEmailChange = (val: string) => {
  // ...
};
```

```tsx
// ✅ Good
const handleEmailChange = (val: string) => {
  // ...
};
```

## Необязательные пропсы

Избегайте передачи значения по умолчанию для необязательного пропса.

**ПРИМЕР**

Возьмите компонент `EmailField`, определенный ниже:

```tsx
type EmailFieldProps = {
  value: string;
  disabled?: boolean;
};

const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
  <TextInput value={value} disabled={disabled} fullWidth />
);
```

**Использование**

```tsx
// ❌ Bad, passing in the same value as the default value adds no value
const Form = () => <EmailField value="username@email.com" disabled={false} />;
```

```tsx
// ✅ Good, assumes the default value
const Form = () => <EmailField value="username@email.com" />;
```

## Компонент как пропсы

Постарайтесь максимально передавать неинстанцированные компоненты как пропсы, чтобы дети могли самостоятельно определять, какие пропсы им нужно передать.

Наиболее распространенный пример этого — иконки компонентов:

```tsx
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;

// In MyComponent
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
  const theme = useTheme();

  return (
    <div>
      <MyIcon size={theme.icon.size.md}>
    </div>
  )
};
```

Чтобы React понял, что компонент является компонентом, нужно использовать PascalCase, чтобы потом инстанцировать его с `<MyIcon>`

## Сверление пропсов: минимизируйте его

Сверление пропсов в контексте React — это практика передачи переменных состояния и их сеттеров через многие уровни компонентов, даже если промежуточные компоненты их не используют. Хотя иногда это необходимо, чрезмерное сверление пропсов может привести к:

1. **Уменьшение читаемости**: Отслеживание происхождения пропса или мест, где он используется, может стать запутанным в глубоко вложенной структуре компонентов.

2. **Трудности в обслуживании**: Изменения в структуре пропсов одного компонента могут потребовать изменений в нескольких компонентах, даже если они их не используют напрямую.

3. **Снижение повторного использования компонентов**: Компонент, получающий много пропсов только для передачи их дальше, становится менее универсальным и сложным для повторного использования в разных контекстах.

Если вы считаете, что чрезмерно используете сверление пропсов, обратите внимание на [лучшие практики управления состоянием](#state-management).

## Импорт

При импорте предпочтение стоит отдать указанным псевдонимам, а не полным или относительным путям.

**Псевдонимы**

```js
{
  alias: {
    "~": path.resolve(__dirname, "src"),
    "@": path.resolve(__dirname, "src/modules"),
    "@testing": path.resolve(__dirname, "src/testing"),
  },
}
```

**Использование**

```tsx
// ❌ Bad, specifies the entire relative path
import {
  CatalogDecorator
} from '../../../../../testing/decorators/CatalogDecorator';
import {
  ComponentDecorator
} from '../../../../../testing/decorators/ComponentDecorator';
```

```tsx
// ✅ Good, utilises the designated aliases
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from 'twenty-ui/testing';
```

## Проверка схемы

[Zod](https://github.com/colinhacks/zod) — это проверка схемы для нетипизированных объектов:

```js
const validationSchema = z
  .object({
    exist: z.boolean(),
    email: z
      .string()
      .email('Email must be a valid email'),
    password: z
      .string()
      .regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
  })
  .required();

type Form = z.infer<typeof validationSchema>;
```

## Изменения с нарушением совместимости

Всегда проводите тщательное ручное тестирование перед тем, как продолжить, чтобы гарантировать, что изменения не вызвали перебоев в других местах, поскольку тесты еще не были широко интегрированы.

